#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>


#include "icar.h"
#include "icar_log.h"
#include "icar_msg.h"
#include "icar_utils.h"

int
icar_create_server(int type, int port, const char *address)
{
	if (type != SOCK_STREAM && type != SOCK_DGRAM)
		return -1;

	struct sockaddr_in addr;
	int fd;

	fd = socket(AF_INET, type, 0);
	if (fd < 0)
		return -1;

	bzero(&addr, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons((uint16_t)port);
	if (!address)
		addr.sin_addr.s_addr = INADDR_ANY;
	else
		addr.sin_addr.s_addr = inet_addr(address);

#ifdef SO_REUSEADDR
	int opt = 1;
	setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
#endif
	if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) 
	{
		close(fd);
		return -1;
	}

	if (listen(fd, 1024) < 0)
	{
		close(fd);
		return -1;
	}

	return fd;
}

request *icar_get_connection(int fd, struct timeval *timeout)
{
	request *new = NULL;
	int result;
	fd_set fds;
	struct sockaddr_in addr;
	socklen_t addrlen;
	char *ipaddr;

	FD_ZERO(&fds);
	FD_SET(fd, &fds);
	result = 0;

	while (result == 0)
	{
		result = select(fd+1, &fds, 0, 0, timeout);
		if (result < 0)
			return NULL;
		if (timeout != 0 && result == 0)
			return NULL;
		if (result > 0)
			break;
	}

	new = (request *)malloc(sizeof(request));
	if (new == NULL) {
		return NULL;
	}

	memset(new, 0, sizeof(request));
	bzero(&addr, sizeof(addr));
	addrlen = sizeof(addr);
	new->clientSock = accept(fd, (struct sockaddr *)&addr, &addrlen);
	ipaddr = inet_ntoa(addr.sin_addr);
	if (ipaddr) {
		strncpy(new->clientAddr, ipaddr, IP_ADDR_LEN);
	} else
		*new->clientAddr = 0;

	new->readBufRemain = 0;
	new->readBufPtr = NULL;

	return new;
}

int
_icar_net_read(sock, buf, len)
	int sock;
	uint8_t *buf;
	int len;
{
#if defined(_WIN32)
	return (recv(sock, buf, len, 0));
#else
	int nfds;
	fd_set readfds;
	struct timeval timeout;

	timeout.tv_sec = 0;
	timeout.tv_usec = 10000;
	nfds = sock+1;

	FD_ZERO(&readfds);
	FD_SET(sock, &readfds);

	nfds = select(nfds, &readfds, NULL, NULL, &timeout);

	if (nfds > 0) {
		return (read(sock, buf, len));
	}

	return nfds;
#endif
}

int
_icar_readChar(request *r, uint8_t *ch)
{
	if (r->readBufRemain == 0) {
		r->readBufRemain = _icar_net_read(r->clientSock, 
				r->readBuf, READ_BUF_LEN);
		if (r->readBufRemain < 1)
			return 0;
		r->readBufPtr = r->readBuf;
	}

	*ch = *r->readBufPtr++;
	r->readBufRemain--;

	return 1;
}

int _icar_readPkg(request *r, uint8_t *destBuf, int len)
{
	uint8_t curChar,
		 *dst;
	int count;

	count = 0;
	dst = destBuf;

	while (count < len)
	{
		if (_icar_readChar(r, &curChar) < 1)
			return 0;

		if (count == 0) {
			if (curChar == 0x7e) {
				count++;
				*dst = curChar;
				continue;
			}
			else {
				*dst = 0;
				return 0;
			}
		}
		if (count == 1) {
			if (curChar == 0x7e) {
				*dst = curChar;
				continue;
			} else {
				dst++;
				*dst++ = curChar;
				count++;
				continue;
			}
		}

		*dst++ = curChar;
		count++;
		if (curChar == 0x7e)
			break;
	}

	return count;
}

int icar_read_request(int fd, request *r) {
	int count;
	static uint8_t buf[READ_BUF_LEN];
	uint16_t id;
	uint16_t property, stmp;
	icarPkg *new;
	int i;

	while ((count = _icar_readPkg(r, buf, READ_MAX_LEN)) > 2)
	{
		if (count > 2082)
			return -1;

		new = (icarPkg *)icar_malloc(sizeof(icarPkg));
		memset(new, 0, sizeof(icarPkg));

		new->len = icar_pkg_del_head_tail(new->data, buf, count);
		if (new->len < 0)
		{
			free(new);
			return -1;
		}

		memcpy(&stmp, &(new->data[0]), 2);
		id = ntohs(stmp);
		new->id = id;

		memcpy(&stmp, &(new->data[2]), 2);
		property = ntohs(stmp);

		for (i = 0; i < ARRAY_SIZE(r->package); i++) {
			if ((r->package[i] != NULL) && (r->package[i]->id == new->id))
			{
				new->next = r->package[i];
				r->package[i] = new;
				break;
			}
		}

		if (i == ARRAY_SIZE(r->package))
			for (i = 0; i < ARRAY_SIZE(r->package); i++) {
				if (r->package[i] == NULL) {
					new->next = r->package[i];
					r->package[i] = new;
					break;
				}
			}

		snprintf(new->number, 6, "%02x%02x%02x%02x%02x%02x",
				new->data[4], new->data[5], new->data[6],
				new->data[7], new->data[8], new->data[9]);
		memcpy(&stmp, &(new->data[10]), 2);

		new->sequence = htons(stmp);

		if (property & SUBPACKAGE)
		{
			memcpy(&stmp, &(new->data[12]), 2);
			new->total = htons(stmp);
			memcpy(&stmp, &(new->data[14]), 2);
			new->count = htons(stmp);
		}
		else
		{
			new->total = 0;
		}
	}

	return 0;
}

int icar_process_request(int fd, request *r)
{
	icarPkg *tmp;
	int i = 0;

	for (i = 0; i < ARRAY_SIZE(r->package); i++) {
		if (r->package[i] != NULL)
		{
			tmp = r->package[i];
			while (tmp != NULL) {
				printf("id: %04x, len: %d\n", tmp->id, tmp->len);
				tmp = tmp->next;
			}
		}
	}

	return 0;
}

void
icar_end_request(request *r)
{
	shutdown(r->clientSock, 2);
	close(r->clientSock);
	icarPkg *tmp, *ttmp;
	int i = 0;
	for (i = 0; i < ARRAY_SIZE(r->package); i++) {
		if (r->package[i] != NULL)
		{
			tmp = r->package[i];
			while (tmp != NULL) {
				ttmp = tmp->next;
				free(tmp);
				tmp = ttmp;
			}
			r->package[i] = NULL;
		}
	}
	free(r);
}
